package com.sl.plugin.step.script;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.*;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.stream.Collectors;


@Slf4j
public class HandlerMethodArgumentResolver {
    public static Object[] resolver(Method method, Method invokeMethod, Callable callable, Object... params) {
        List vc = Lists.newArrayList();
        vc.add(invokeMethod);
        vc.add(callable);

        if (ArrayUtil.isNotEmpty(params)) {
            for (Object arg : params) {
                vc.add(arg);
            }
        }

        return resolver(method, Arrays.stream(params).collect(Collectors.toList()), vc.toArray());
    }

    public static Object[] resolver(Method method, Object... params) {

        return resolver(method, Lists.newArrayList(), params);
    }

    public static Object[] resolverList(Method method, Collection collection) {
        return resolver(method, collection, new Object[0]);
    }

    public static Object[] resolver(Method method, Collection<Object> args, Object... params) {
        if(ObjectUtil.isNull(method)){
            return new Object[]{};
        }
        //先获取方法的参数
        Parameter[] parameters = method.getParameters();

        if (ArrayUtil.isEmpty(parameters)) {
            return new Object[]{};
        }
        List<Object> lists = Lists.newArrayList();

        for (Parameter parameter : parameters) {
            String name = parameter.getName();
            Class<?> type = parameter.getType();
            if (StrUtil.equals(name, "args")) {
                lists.add(args);
                continue;
            }
            Object objByArguments = findObjByArguments(name, type, params);
            lists.add(objByArguments);
        }
        return lists.toArray(new Object[0]);
    }

    private static Object findObjByArguments(String name, Class<?> type, Object... params) {
        if (ArrayUtil.isEmpty(params)) {
            return null;
        }
        for (Object param : params) {
            Object obj = findObj(name, type, param);
            if (ObjectUtil.isNotNull(obj)) {
                return obj;
            }
        }
        return null;
    }

    private static Object findObj(String name, Class<?> type, Object params) {

        if (ObjectUtil.isNull(params)) {
            return null;
        }
        if (params instanceof Map) {
            //先只处理一级
            Map<?, ?> map = (Map<?, ?>) params;
            Object o = MapUtil.get(map, name, type);
            return o;
        } else if (params instanceof Collection) {

        } else if (params instanceof Method) {
            if (type.equals(Method.class)) {
                return params;
            }
        } else if (params instanceof Callable) {
            if (type.equals(Callable.class)) {
                return params;
            }
        } else if (!ClassUtil.isSimpleTypeOrArray(params.getClass())) {

            Field[] fields = ReflectUtil.getFields(params.getClass());

            Object object = null;
            for (Field field : fields) {
                String fieldName = field.getName();

                if (StrUtil.equals(fieldName, name)) {
                    object = ReflectUtil.getFieldValue(params, field);
                    try {
                        object = Convert.convert(type, object);
                    } catch (Exception e) {
                       e.printStackTrace();
                    }
                    break;
                } else if (!ClassUtil.isSimpleTypeOrArray(field.getType())) {
                    object = findObj(name, type, ReflectUtil.getFieldValue(params, field));
                    if (ObjectUtil.isNotNull(object)) {
                        break;
                    }
                }
            }
            return object;

        }
        return null;
    }
}
